﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Director.DomainModels;
using Director.StockSharp.Transaq.Native.Responses;
using Board = Director.DomainModels.Board;
using Position = Director.DomainModels.Position;
using Market = Director.DomainModels.Market;

namespace Director
{
	class DataHolder
	{
		public EServiceMode Mode { get; set; }

		public DataHolder()
		{
			Mode = EServiceMode.Fix;

			TimeFrames = new ConcurrentDictionary<int, TimeFrame>();
			Markets = new ConcurrentDictionary<int, Market>();
			Securities = new ConcurrentDictionary<int, Security>();
			Boards = new ConcurrentDictionary<string, Board>();
			Positions = new ConcurrentDictionary<int, Position>();
			PipeStorage = new PipeStorage();
		}

		public PipeStorage PipeStorage { get; private set; }
		public ConcurrentDictionary<string, Board> Boards { get; private set; }
		public ConcurrentDictionary<int, TimeFrame> TimeFrames { get; private set; }
		public ConcurrentDictionary<int, Market> Markets { get; private set; }
		public ConcurrentDictionary<int, Security> Securities { get; private set; }
		public ConcurrentDictionary<int, Position> Positions { get; private set; }
		public Action<int, int> CandlesUpdatedAction { get; set; }
		public Action<EDataKind, int> DataReceivedAction { get; set; }
		public Action<Trade> OnNewTradeAction { get; set; }
		public Action<Order> OnNewOrderAction { get; set; }

		public void Clear()
		{
			Boards.Clear();
			TimeFrames.Clear();
			Markets.Clear();
			Securities.Clear();
			Positions.Clear();
		}

		public void OnNewCandleKindsResponse(CandleKindsResponse candleKindsResponse)
		{
			foreach (var candleKind in candleKindsResponse.Kinds)
			{
				TimeFrames.GetOrAdd(candleKind.Id, new TimeFrame(candleKind));
			}
		}

		public void OnNewSecInfoResponse(SecInfoResponse obj)
		{
		}

		public void OnNewSecuritiesResponse(SecuritiesResponse obj)
		{
			foreach (var transaqSecurity in obj.Securities)
			{
				Securities.GetOrAdd(transaqSecurity.SecId, new Security(transaqSecurity));
			}
		}

		public void OnNewPitsResponse(PitsResponse obj)
		{
			foreach (var pit in obj.Pits)
			{
			}
		}

		public void OnNewClientResponse(ClientResponse obj)
		{
			
		}

		public void OnNewPositionsResponse(PositionsResponse obj)
		{
			var keys = Positions.Keys.ToList();
			foreach (var secPosition in obj.SecPositions)
			{
				var newPos = new Position(secPosition);
				var oldPos = Positions.GetOrAdd(secPosition.SecId, newPos);
				if (oldPos.Saldo != newPos.Saldo)
				{
					Positions.TryUpdate(secPosition.SecId, newPos, oldPos);
				}
				else
				{
					if (!keys.Remove(secPosition.SecId))
					{
						keys.Add(secPosition.SecId);
					}
				}
			}
			foreach (var moneyPosition in obj.MoneyPositions)
			{
				var newPos = new Position(moneyPosition);
				var oldPos = Positions.GetOrAdd(0, newPos);
				if (oldPos.Saldo != newPos.Saldo)
				{
					Positions.TryUpdate(0, newPos, oldPos);
				}
				else
				{
					if (!keys.Remove(0))
					{
						keys.Add(0);
					}
				}
			}
			if (keys.Any())
			{
				DataReceivedAction(EDataKind.Positions, -1);
			}
		}

		public void OnNewOvernightResponse(OvernightResponse obj)
		{
			
		}

		public void OnNewMarketsResponse(MarketsResponse obj)
		{
			foreach (var market in obj.Markets)
			{
				Markets.GetOrAdd(market.Id, new Market(market));
			}
		}

		public void OnNewBoardsResponse(BoardsResponse obj)
		{
			foreach (var board in obj.Boards)
			{
				Boards.GetOrAdd(board.Id, new Board(board));
			}
		}


		public void OnNewCandlesResponse(CandlesResponse response)
		{
			Console.WriteLine(response.Candles.Count());
			PipeStorage.Write(response, TimeFrames[response.Period].TimeFrameLength);
			var security = Securities.Single(e => e.Value.Board == response.Board && e.Value.Code == response.SecCode).Value;
			CandlesUpdatedAction(security.Id, TimeFrames[response.Period].TimeFrameLength);
		}

		public IEnumerable<Candle> ReadCandles(int securityId, int timeframeId, DateTime from)
		{
			return PipeStorage.ReadCandles(Securities[securityId], TimeFrames[timeframeId].TimeFrameLength, from).OrderBy(e=>e.Date);
		}

		public Candle ReadLastCandle(int securityId, int timeframeId, DateTime lastOnDate)
		{
			return PipeStorage.ReadLastCandle(Securities[securityId], TimeFrames[timeframeId].TimeFrameLength, lastOnDate);
		}

		public void OnNewMessagesResponse(MessagesResponse obj)
		{
			
		}

		public void OnNewQuotationsResponse(QuotationsResponse obj)
		{
			
		}

		public long GetCandlesAbsentTillNow(string secCode, string boardId, int timeFrameId)
		{
			var security = Securities.Values.Single(e => e.Code == secCode && e.Board == boardId);

			var timeFrameLength = TimeFrames[timeFrameId].TimeFrameLength;

			var lastDate = PipeStorage.GetLastCandleLastDate(secCode, boardId, timeFrameLength);
			var now = DateTime.Now;
			if (now.Hour > 19 && security.Sectype==SecurityTypes.SHARE)
			{
				now = now.Date + TimeSpan.FromHours(19);
			}
			var result = (lastDate == null || TXmlConnector.DllProxy is DllProxyPlayer) ? 117001 : GetКоличествоНеобходимыхСвечей(now, lastDate, timeFrameLength);
			return Math.Max(0, result);
		}

		private static long GetКоличествоНеобходимыхСвечей(DateTime now, DateTime? lastDate, int timeFrameLength)
		{
			if (!lastDate.HasValue) return MyConnector.FIX_LENGTH;
			if (lastDate.Value.Date == now.Date)
			{
				return (long)(now - lastDate.Value).TotalSeconds/timeFrameLength + 2;
			}
			var hours = ((now - lastDate.Value).Days+1)*20;
			return hours * 60 * 60 / timeFrameLength;
		}

		public void SetMode(EServiceMode mode)
		{
			Mode = mode;
		}

		public void OnNewTradesResponse(TradesResponse trades)
		{
			foreach (var trade in trades.Trades)
			{
				OnNewTradeAction(new Trade(trade));
			}
		}

		public void OnNewOrdersResponse(OrdersResponse orders)
		{
			foreach (var order in orders.Orders)
			{
				OnNewOrderAction(new Order(order));
			}
		}
	}
}
